/**
 * Copyright (c) 2011-2022 Bill Greiman
 * This file is part of the SdFat library for SD memory cards.
 *
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include <stddef.h>
#include "upcase.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
#define TABLE_MEM PROGMEM
#define readTable8(sym)  pgm_read_byte(&sym)
#define readTable16(sym) pgm_read_word(&sym)
#else  // __AVR__
#define TABLE_MEM
#define readTable8(sym)  (sym)
#define readTable16(sym) (sym)
#endif  // __AVR__

struct map16 {
  uint16_t base;
  int8_t off;
  uint8_t count;
};
typedef struct map16 map16_t;

struct pair16 {
  uint16_t key;
  uint16_t val;
};
typedef struct pair16 pair16_t;
//------------------------------------------------------------------------------
static const map16_t mapTable[] TABLE_MEM = {
  {0X0061, -32,  26},
  {0X00E0, -32,  23},
  {0X00F8, -32,  7 },
  {0X0100,   1,  48},
  {0X0132,   1,   6},
  {0X0139,   1,  16},
  {0X014A,   1,  46},
  {0X0179,   1,   6},
  {0X0182,   1,   4},
  {0X01A0,   1,   6},
  {0X01B3,   1,   4},
  {0X01CD,   1,  16},
  {0X01DE,   1,  18},
  {0X01F8,   1,  40},
  {0X0222,   1,  18},
  {0X0246,   1,  10},
  {0X03AD, -37,   3},
  {0X03B1, -32,  17},
  {0X03C3, -32,   9},
  {0X03D8,   1,  24},
  {0X0430, -32,  32},
  {0X0450, -80,  16},
  {0X0460,   1,  34},
  {0X048A,   1,  54},
  {0X04C1,   1,  14},
  {0X04D0,   1,  68},
  {0X0561, -48,  38},
  {0X1E00,   1, 150},
  {0X1EA0,   1,  90},
  {0X1F00,   8,   8},
  {0X1F10,   8,   6},
  {0X1F20,   8,   8},
  {0X1F30,   8,   8},
  {0X1F40,   8,   6},
  {0X1F60,   8,   8},
  {0X1F70,  74,   2},
  {0X1F72,  86,   4},
  {0X1F76, 100,   2},
  {0X1F7A, 112,   2},
  {0X1F7C, 126,   2},
  {0X1F80,   8,   8},
  {0X1F90,   8,   8},
  {0X1FA0,   8,   8},
  {0X1FB0,   8,   2},
  {0X1FD0,   8,   2},
  {0X1FE0,   8,   2},
  {0X2170, -16,  16},
  {0X24D0, -26,  26},
  {0X2C30, -48,  47},
  {0X2C67,   1,   6},
  {0X2C80,   1, 100},
  {0X2D00,   0,  38},
  {0XFF41, -32,  26},
};
const size_t MAP_DIM = sizeof(mapTable)/sizeof(map16_t);
//------------------------------------------------------------------------------
static const pair16_t lookupTable[] TABLE_MEM = {
  {0X00FF, 0X0178},
  {0X0180, 0X0243},
  {0X0188, 0X0187},
  {0X018C, 0X018B},
  {0X0192, 0X0191},
  {0X0195, 0X01F6},
  {0X0199, 0X0198},
  {0X019A, 0X023D},
  {0X019E, 0X0220},
  {0X01A8, 0X01A7},
  {0X01AD, 0X01AC},
  {0X01B0, 0X01AF},
  {0X01B9, 0X01B8},
  {0X01BD, 0X01BC},
  {0X01BF, 0X01F7},
  {0X01C6, 0X01C4},
  {0X01C9, 0X01C7},
  {0X01CC, 0X01CA},
  {0X01DD, 0X018E},
  {0X01F3, 0X01F1},
  {0X01F5, 0X01F4},
  {0X023A, 0X2C65},
  {0X023C, 0X023B},
  {0X023E, 0X2C66},
  {0X0242, 0X0241},
  {0X0253, 0X0181},
  {0X0254, 0X0186},
  {0X0256, 0X0189},
  {0X0257, 0X018A},
  {0X0259, 0X018F},
  {0X025B, 0X0190},
  {0X0260, 0X0193},
  {0X0263, 0X0194},
  {0X0268, 0X0197},
  {0X0269, 0X0196},
  {0X026B, 0X2C62},
  {0X026F, 0X019C},
  {0X0272, 0X019D},
  {0X0275, 0X019F},
  {0X027D, 0X2C64},
  {0X0280, 0X01A6},
  {0X0283, 0X01A9},
  {0X0288, 0X01AE},
  {0X0289, 0X0244},
  {0X028A, 0X01B1},
  {0X028B, 0X01B2},
  {0X028C, 0X0245},
  {0X0292, 0X01B7},
  {0X037B, 0X03FD},
  {0X037C, 0X03FE},
  {0X037D, 0X03FF},
  {0X03AC, 0X0386},
  {0X03C2, 0X03A3},
  {0X03CC, 0X038C},
  {0X03CD, 0X038E},
  {0X03CE, 0X038F},
  {0X03F2, 0X03F9},
  {0X03F8, 0X03F7},
  {0X03FB, 0X03FA},
  {0X04CF, 0X04C0},
  {0X1D7D, 0X2C63},
  {0X1F51, 0X1F59},
  {0X1F53, 0X1F5B},
  {0X1F55, 0X1F5D},
  {0X1F57, 0X1F5F},
  {0X1F78, 0X1FF8},
  {0X1F79, 0X1FF9},
  {0X1FB3, 0X1FBC},
  {0X1FCC, 0X1FC3},
  {0X1FE5, 0X1FEC},
  {0X1FFC, 0X1FF3},
  {0X214E, 0X2132},
  {0X2184, 0X2183},
  {0X2C61, 0X2C60},
  {0X2C76, 0X2C75},
};
const size_t LOOKUP_DIM = sizeof(lookupTable)/sizeof(pair16_t);
//------------------------------------------------------------------------------
static size_t searchPair16(const pair16_t* table, size_t size, uint16_t key) {
  size_t left = 0;
  size_t right = size;
  size_t mid;
  while (right - left > 1) {
    mid = left + (right - left)/2;
    if (readTable16(table[mid].key) <= key) {
      left = mid;
    } else {
      right = mid;
    }
  }
  return left;
}
//------------------------------------------------------------------------------
uint16_t toUpcase(uint16_t chr) {
  uint16_t i, first;
  // Optimize for simple ASCII.
  if (chr < 127) {
    return chr - ('a' <= chr && chr <= 'z' ? 'a' - 'A' : 0);
  }
  i = searchPair16(reinterpret_cast<const pair16_t*>(mapTable), MAP_DIM, chr);
  first = readTable16(mapTable[i].base);
  if (first <= chr && (chr - first)  < readTable8(mapTable[i].count)) {
    int8_t off = readTable8(mapTable[i].off);
    if (off == 1) {
      return chr - ((chr - first) & 1);
    }
    return chr + (off ? off : -0x1C60);
  }
  i = searchPair16(lookupTable, LOOKUP_DIM, chr);
  if (readTable16(lookupTable[i].key) == chr) {
    return readTable16(lookupTable[i].val);
  }
  return chr;
}
//------------------------------------------------------------------------------
uint32_t upcaseChecksum(uint16_t uc, uint32_t sum) {
  sum = (sum << 31) + (sum >> 1) + (uc & 0XFF);
  sum = (sum << 31) + (sum >> 1) + (uc >> 8);
  return sum;
}
